package com.liu.aspect;

import com.liu.annotation.Log;
import com.liu.core.RedisTemplate;
import com.liu.entity.LiuOperLog;
import com.liu.service.LiuOperLogService;
import com.liu.util.AuthUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.swing.plaf.PanelUI;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName LogAspect
 * @Author liuxin
 * @Date Created in 2023-02-01 09:57
 * @Version 1.0
 */

@Slf4j
@Aspect
@Component
//public class LogAspect implements BeanFactoryAware {
public class LogAspect {


    @Resource
    private LiuOperLogService liuOperLogService;

    @Resource
    private RedisTemplate redisTemplate;

//    @Resource
//    private ExecutorService executorService;

//    这里不需要注入，它会自动调用,我不注入失败，注入成功
//    @Resource
//    private BeanFactory beanFactory;
//
//    @Override
//    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
//
//    }

    @AfterReturning("@annotation(operLog)")
    public void afterReturning(JoinPoint joinPoint, Log operLog) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        System.out.println("main----" + Thread.currentThread().getName());

        LiuOperLog liuOperLog = createOperLog(joinPoint, request, operLog, null);

        //这里的生成对象是一个代理对象,因为方法有注解，且代理一定会有增强，那么这里就一定会调用的是它的增强
//        LogAspect logAspect = beanFactory.getBean(this.getClass());
//        logAspect.logHandler(liuOperLog);
        liuOperLogService.insert(liuOperLog);
        log.info("{} 执行了 {} ",liuOperLog.getOperName(),liuOperLog.getTitle());
    }

    @AfterThrowing(value = "@annotation(operLog)", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Log operLog, Exception exception) {

        System.out.println(exception.getMessage());


        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        LiuOperLog liuOperLog = createOperLog(joinPoint, request, operLog, exception);
//        LogAspect logAspect = beanFactory.getBean(this.getClass());

//        logHandler(liuOperLog);
        liuOperLogService.insert(liuOperLog);
        log.info("{} 执行了 {} ",liuOperLog.getOperName(),liuOperLog.getTitle());

    }


    /**
     * spring的异步任务
     */
    //这里写入配置的池子名字
//    @Async("liu-logger")
//    public void logHandler(LiuOperLog liuOperLog) {

//        System.out.println("main----" + Thread.currentThread().getName());


    //保存日志对象
    //如果这个线程包裹全部，获取request会出现异常，因为获取方式查看源码可以发现是在ThreadLocal里面，会有跨线程问题，而这里
    //直接包裹核心insert即可，本来也只是插入会产生问题，刚好解决问题，
//        executorService.execute(() -> {
//            liuOperLogService.insert(liuOperLog);
//            System.out.println("insert完成");

//        };


//    }
    private LiuOperLog createOperLog(JoinPoint joinPoint, HttpServletRequest request, Log log, Exception exception) {
//        System.out.println("log----" + Thread.currentThread().getName());

        System.out.println("日志开始");
        //1.根据现场信息，封装日志实例
        LiuOperLog liuOperLog = new LiuOperLog();
        liuOperLog.setTitle(log.title());
        liuOperLog.setBusinessType(log.businessType());
        if (exception != null) {
            liuOperLog.setErrormsg(exception.getMessage().length() > 1000 ?
                    exception.getMessage().substring(0, 1000) : exception.getMessage());
            liuOperLog.setStatus(500);
        } else {
            liuOperLog.setStatus(200);
        }

        //request当中获取一些信息
        liuOperLog.setOperIp(request.getRemoteAddr());
        System.out.println("request.getRemoteAddr() = " + liuOperLog.getOperIp());
        liuOperLog.setRequestMethod(request.getMethod());

        //这里可能会有空指针问题
        if (AuthUtil.getLoginUser(redisTemplate) != null && AuthUtil.getLoginUser(redisTemplate).getLiuUser() != null) {
            System.out.println("无空指针问题");
            liuOperLog.setOperName(AuthUtil.getLoginUser(redisTemplate).getLiuUser().getUserName());
        }

        liuOperLog.setOperUrl(request.getRequestURI());

        //获取执行的方法(切点获取签名，获取get)
        String methodName = joinPoint.getSignature().getName();
        liuOperLog.setMethod(methodName);
        liuOperLog.setOpertime(new Date());

        System.out.println("日志对象" + liuOperLog.toString());
        return liuOperLog;
    }


    /**
     * 自定义线程池实现异步
     */

//    private void logHandler(JoinPoint joinPoint, HttpServletRequest request, Log log, Exception exception) {
//
//        //下面的写法，当大量的请求，会导致线程数突增，所以需要使用到线程池
////        new Thread(()->{
////
////        }).start();
//
//        //这里是一个无界队列，可以点进去看，所以如果任务过多，可能会产生内存溢出
//        //这样写也不行，目的是每次获取一个而不是重新创建一个
////        ExecutorService executorService = Executors.newFixedThreadPool(10);
//        //提交任务
//        System.out.println("main----" + Thread.currentThread().getName());
//
//
//        System.out.println("log----" + Thread.currentThread().getName());
//        System.out.println("日志开始");
//        //1.根据现场信息，封装日志实例
//        LiuOperLog liuOperLog = new LiuOperLog();
//        liuOperLog.setTitle(log.title());
//        liuOperLog.setBusinessType(log.businessType());
//        if (exception != null) {
//            liuOperLog.setErrormsg(exception.getMessage().length() > 1000 ?
//                    exception.getMessage().substring(0, 1000) : exception.getMessage());
//            liuOperLog.setStatus(500);
//        } else {
//            liuOperLog.setStatus(200);
//        }
//
//        //request当中获取一些信息
//        liuOperLog.setOperIp(request.getRemoteAddr());
//        System.out.println("request.getRemoteAddr() = " + liuOperLog.getOperIp());
//        liuOperLog.setRequestMethod(request.getMethod());
//
//        //这里可能会有空指针问题
//        if (AuthUtil.getLoginUser(redisTemplate) != null && AuthUtil.getLoginUser(redisTemplate).getLiuUser() != null) {
//            System.out.println("无空指针问题");
//            liuOperLog.setOperName(AuthUtil.getLoginUser(redisTemplate).getLiuUser().getUserName());
//        }
//
//        liuOperLog.setOperUrl(request.getRequestURI());
//
//        //获取执行的方法(切点获取签名，获取get)
//        String methodName = joinPoint.getSignature().getName();
//        liuOperLog.setMethod(methodName);
//        liuOperLog.setOpertime(new Date());
//
//        System.out.println("日志对象" + liuOperLog.toString());
//        //保存日志对象
//        //如果这个线程包裹全部，获取request会出现异常，因为获取方式查看源码可以发现是在ThreadLocal里面，会有跨线程问题，而这里
//        //直接包裹核心insert即可，本来也只是插入会产生问题，刚好解决问题，
//        executorService.execute(() -> {
//            liuOperLogService.insert(liuOperLog);
//            System.out.println("insert完成");
//
//        });
//
//
//    }

}
